Skip to content

Fix: Main thread blocked for ~1s during WebRTC connection setup #834

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from

Conversation

sampepose
Copy link

@sampepose sampepose commented Jun 6, 2025

🎯 Goal

When joining a call, especially the first call after app launch, the UI freezes for approximately 1 second. This creates a poor user experience with an unresponsive interface.

📝 Summary

Root Cause

  • setLocalDescription and setRemoteDescription are marked with @MainActor
  • The underlying WebRTC implementation performs a blocking network call during these operations
  • When called from MainActor context, this noticeably blocks the main thread for ~1-2s depending on network conditions

Instrumentation Graphs

See here there is a 1 second hang in instrumentation:
Screenshot 2025-06-06 at 2 22 36 AM

WebRTC Code Trace

  1. setLocalDescription is defined in RTCPeerConnection: https://github.com/GetStream/webrtc/blob/main/sdk/objc/api/peerconnection/RTCPeerConnection.mm#L595-L601
  2. _peerConnection is a rtc::scoped_refptr<webrtc::PeerConnectionInterface>, with it's concrete SetLocalDescription defined here: https://github.com/GetStream/webrtc/blob/main/pc/peer_connection.cc#L1494-L1499
  3. sdp_handler_ is a SdpOfferAnswerHandler, with it's SetLocalDescription defined here: https://github.com/GetStream/webrtc/blob/main/pc/sdp_offer_answer.cc#L1555-L1580
  4. This invokes DoSetLocalDescription (https://github.com/GetStream/webrtc/blob/main/pc/sdp_offer_answer.cc#L1542C24-L1545) which then invokes PushdownTransportDescription (https://github.com/GetStream/webrtc/blob/main/pc/sdp_offer_answer.cc#L1696)
  5. PushdownTransportDescription finally calls SetLocalDescription on the transport_controller_s which is a JsepTransportController: https://github.com/GetStream/webrtc/blob/main/pc/sdp_offer_answer.cc#L4909-L4910
  6. This finally does a blocking network call: https://github.com/GetStream/webrtc/blob/main/pc/jsep_transport_controller.cc#L77-L100

🛠 Implementation

  1. Remove @MainActor from both methods to allow them to run on any thread
  2. Add thread-safe queue serialization to prevent race conditions in WebRTC operations

Why Queue Serialization is Necessary

Removing @MainActor alone introduces race conditions when multiple threads access the RTCPeerConnection simultaneously. Manual testing showed:

  • Without queue: Video fails to establish ~70% of the time
  • With queue: Video establishes reliably 100% of the time

The queue maintains the thread safety that @MainActor provided, but without blocking the main thread.

🧪 Manual Testing Notes

  1. Join an existing call from iOS
  2. Baseline: 1 second UI freeze, video sometimes fails to establish
  3. With fix: No UI freeze, video reliably establishes

Testing Results (10 attempts each):

  • Baseline: 1s hang, video issues persist
  • @mainactor removed only: No hang, but video only works 3/10 times
  • @mainactor removed + queue added: No hang, video works 10/10 times

☑️ Contributor Checklist

  • [x ] I have signed the Stream CLA (required)
  • [x ] This change follows zero ⚠️ policy (required)
  • This change should receive manual QA
  • Changelog is updated with client-facing changes
  • New code is covered by unit tests
  • Comparison screenshots added for visual changes
  • Affected documentation updated (tutorial, CMS)

@sampepose sampepose requested a review from a team as a code owner June 6, 2025 06:26
sampepose added 2 commits June 7, 2025 16:00
- Remove @mainactor from setLocalDescription and setRemoteDescription
- Fixes ~1s UI freeze when joining calls after app launch
- Add connectionQueue to serialize all RTCPeerConnection access
- Prevent 968ms UI hangs by moving WebRTC operations off main thread
- Maintain HasRemoteDescription event for proper video flow
- Fix thread safety for addTransceiver, restartIce, and close operations
@sampepose sampepose force-pushed the thread-blocking-fix branch from 75c89ae to 322e4e4 Compare June 7, 2025 20:01
@ipavlidakis
Copy link
Contributor

Hey @sampepose, thank you for this PR. We are currently in the process of resolving many performance issues (you can take see a sneak peek in the open PRs).

We are now in a state where we have resolved all the hungs and for that reason I'm not going to accept this PR.

I'm going to keep open though as I reminder to check again once the changes have been merged.

Thanks,
Ilias

@sampepose
Copy link
Author

I don't think this is addressed with #825 since it still calls these WebRTC functions on the MainActor, blocking it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants